local skynet = require "skynet"
local netpack = require "skynet.netpack"
local socketdriver = require "skynet.socketdriver"

local gateserver = {}

local socket	-- listen socket
local queue		-- message queue
local maxclient	-- max client
local client_number = 0
local CMD = setmetatable({}, { __gc = function() netpack.clear(queue) end })
local nodelay = false --为true时，使用TCP_NODELAY，禁用tcp的Nagle算法，因为Nagle算法会对小数据包做合并，合并到一定大小后才发送出去，这在网游中会造成时延

local connection = {}
-- true : connected
-- nil : closed
-- false : close read

function gateserver.openclient(fd)
	if connection[fd] then
		socketdriver.start(fd)
	end
end

function gateserver.closeclient(fd)
	local c = connection[fd]
	if c ~= nil then
		connection[fd] = nil
		socketdriver.close(fd)
	end
end

function gateserver.start(handler)
	assert(handler.message)
	assert(handler.connect)

----------- CMD为gate服务对于"lua"类型消息的处理集 -----------
	
	function CMD.open( source, conf )
		assert(not socket)
		local address = conf.address or "0.0.0.0"
		local port = assert(conf.port)
		maxclient = conf.maxclient or 1024
		nodelay = conf.nodelay
		skynet.error(string.format("Listen on %s:%d", address, port))

		--调用C层函数监听
		socket = socketdriver.listen(address, port)
		socketdriver.start(socket)
		if handler.open then
			return handler.open(source, conf) --gate此处消息的源服务是watchdog
		end
	end

	function CMD.close()
		assert(socket)
		socketdriver.close(socket)
	end

------------ MSG为gate服务对于网络层消息的处理集 ------------

	local MSG = {}

	local function dispatch_msg(fd, msg, sz)
		if connection[fd] then
			handler.message(fd, msg, sz)
		else
			skynet.error(string.format("Drop message from fd (%d) : %s", fd, netpack.tostring(msg,sz)))
		end
	end

	--netpack收到集到 一个完整的数据包时触发
	MSG.data = dispatch_msg

	local function dispatch_queue()
		local fd, msg, sz = netpack.pop(queue)
		if fd then
			-- may dispatch even the handler.message blocked
			-- If the handler.message never block, the queue should be empty, so only fork once and then exit.
			skynet.fork(dispatch_queue)
			dispatch_msg(fd, msg, sz)

			for fd, msg, sz in netpack.pop, queue do
				dispatch_msg(fd, msg, sz)
			end
		end
	end

	--netpack收到集到 多个完整的数据包时触发
	MSG.more = dispatch_queue

	--对接于网络层的SKYNET_SOCKET_TYPE_ACCEPT消息，当有新的客户端被accept时触发，参数msg通常为对端网络地址
	function MSG.open(fd, msg)
		if client_number >= maxclient then
			socketdriver.shutdown(fd)
			return
		end
		if nodelay then
			socketdriver.nodelay(fd)
		end
		connection[fd] = true
		client_number = client_number + 1
		handler.connect(fd, msg)
	end

	--对接于网络层的SKYNET_SOCKET_TYPE_CLOSE消息，客户端断开连接触发
	function MSG.close(fd)
		if fd ~= socket then
			client_number = client_number - 1
			if connection[fd] then
				connection[fd] = false	-- close read
			end
			if handler.disconnect then
				handler.disconnect(fd)
			end
		else
			socket = nil
		end
	end

	--对接于网络层SKYNET_SOCKET_TYPE_ERROR消息
	function MSG.error(fd, msg)
		if fd == socket then
			skynet.error("gateserver accept error:", msg)
		else
			socketdriver.shutdown(fd)
			if handler.error then
				handler.error(fd, msg)
			end
		end
	end

	--SKYNET_SOCKET_TYPE_WARNING
	function MSG.warning(fd, size)
		if handler.warning then
			handler.warning(fd, size)
		end
	end

	--gate服务注册PTYPE_SOCKET消息的 解包函数 和 消息处理函数: MSG[type]
	skynet.register_protocol {
		name = "socket",
		id = skynet.PTYPE_SOCKET,	-- PTYPE_SOCKET = 6
		unpack = function ( msg, sz )
			return netpack.filter( queue, msg, sz)
		end,
		dispatch = function (_, _, q, type, ...)
			queue = q --C层的lfilter函数也没有对这个先前由netpack.filter(...)推到堆栈上的queue做处理，不明白这么写的用意，只是为了方便查错吧？2333
			if type then
				MSG[type](...)
			end
		end
	}

	--gate服务启动流程中最终调用的函数
	local function init()
		--对于其他snlua服务发送过来的消息，优先用local CMD中的函数去处理，若没有，则用handler.command提供的
		skynet.dispatch("lua", function (_, address, cmd, ...)
			local f = CMD[cmd]
			if f then
				skynet.ret(skynet.pack(f(address, ...)))
			else
				skynet.ret(skynet.pack(handler.command(cmd, address, ...)))
			end
		end)
	end

	--嵌入 直接执行
	if handler.embed then
		init()
	else
		skynet.start(init)
	end
end

return gateserver